/*
 * Copyright (c) 2016- 2020 qolome.com Inc. All Rights Reserved.
 * ProjectName: qolome-knowledge
 * FileName: ClassBuilder.java
 * Author: jiangfeng@qolome.com
 * Date: 2020/3/3 下午6:19
 * LastModified: 2020/03/03 18:01:03
 */

package com.qolome.knowledge.design.pattern.proxy.demo.classes;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Qolome
 * @version V1.0
 * @Description 类文件构建者
 * @date 2020年 03月03日 17:59:44
 */

@Slf4j
public class ClassBuilder {

    public static final String Ln = "\r\n";
    public static final String END = ";" + Ln;
    public static final String IMPORT = "import ";
    private String packageName;
    private ArrayList<String> importList = new ArrayList<>();
    private String className;
    private String extendsName;
    private ArrayList<String> implementList = new ArrayList<>();
    private ArrayList<String> methodList = new ArrayList<>();
    private ArrayList<String> fieldList = new ArrayList<>();


    public ClassBuilder packageName(String value) {
        if (StringUtils.isNotBlank(value)) {
            this.packageName = value;
        } else {
            throw new RuntimeException("包名不能为空");
        }
        return this;
    }


    public ClassBuilder addImport(String value) {
        if (StringUtils.isNotBlank(value)) {
            importList.add(value);
        }
        return this;
    }

    public ClassBuilder className(String value) {
        if (StringUtils.isNotBlank(value)) {
            this.className = value;
        } else {
            throw new RuntimeException("类名不能为空");
        }
        return this;
    }

    public ClassBuilder extendsName(String value) {
        if (StringUtils.isNotBlank(value)) {
            this.extendsName = value;
        }
        return this;
    }

    public ClassBuilder addImplement(String value) {
        if (StringUtils.isNotBlank(value)) {
            implementList.add(value);
        }
        return this;
    }

    public ClassBuilder field(String value) {
        if (StringUtils.isNotBlank(value)) {
            fieldList.add(value);
        }
        return this;
    }

    public ClassBuilder method(String name, String parameter, String content) {
        if (StringUtils.isNotBlank(name)) {
            methodList.add(name + " (" + parameter + ") " + "{" + Ln + content + Ln + "}" + Ln);
        }
        return this;
    }

    public ClassBuilder methods(Method[] methods, String proxyClassName) {
        List<String> methodes = new ArrayList<>(methods.length);
        for (Method method : methods) {
            Class<?>[] params = method.getParameterTypes();
            int paramsLength = params.length;
            StringBuilder containsParams = new StringBuilder();
            StringBuilder containsType = new StringBuilder();
            StringBuilder containsValue = new StringBuilder();
            for (int i = 0; i < paramsLength; i++) {
                Class clazz = params[i];
                String type = clazz.getSimpleName();
                String paramName = toLowerFirstCase(clazz.getSimpleName()) + "Value";
                containsParams.append(type).append(" ").append(paramName).append(", ");
                containsType.append(clazz.getName()).append(".class, ");
                containsValue.append(paramName).append(", ");
            }
            methodes.add(
                    "public "
                            + method.getReturnType().getSimpleName()
                            + " " + method.getName() + "(" + StringUtils.removeEnd(containsParams.toString(), ", ") + ")" + "{" + Ln
                            + "try {" + Ln
                            + "Method m = " + proxyClassName + ".class.getMethod(\"" + method.getName() + "\", new Class[]{" + StringUtils.removeEnd(containsType.toString(), ", ") + "})" + END
                            + checkReturn(method.getReturnType())
                            + convertType("this.h.invoke(this,m,new Object[]{" + StringUtils.removeEnd(containsValue.toString(), ", ") + "})", method.getReturnType())
                            + "}catch (Error _ex){ }" + Ln
                            + "catch (Throwable throwable){" + Ln
                            + "throw new UndeclaredThrowableException(throwable)" + END
                            + "}" + Ln
                            + returnEmptyCode(method.getReturnType())
                            + "}" + Ln
            );
        }
        methodList.addAll(methodes);
        return this;
    }

    private String convertType(String code, Class<?> value) {
            return "(" + ClassAssist.getReturnPackageType(value) + code +  ")"+ ClassAssist.getReturnConvert(value)+END;
    }

    private String returnEmptyCode(Class<?> returnType) {
        if (byte.class == returnType || short.class == returnType || int.class == returnType) {
            return "return 0"+END;
        } else if (long.class == returnType) {
            return "return 0l"+END;
        } else if (float.class == returnType) {
            return "return 0.0f"+END;
        } else if (boolean.class == returnType) {
            return "return false"+END;
        } else if (void.class == returnType) {
            return "";
        }
        return "return null"+END;
    }

    private String checkReturn(Class<?> returnType) {
        return returnType != void.class ? "return " : "";
    }

    private String toLowerFirstCase(String simpleName) {

        if (StringUtils.isAllUpperCase(String.valueOf(simpleName.charAt(0)))) {
            char[] chars = simpleName.toCharArray();
            chars[0] += 32;
            return String.valueOf(chars);
        }
        return simpleName;
    }

    private String toUpperFirstCase(String simpleName) {
        if (StringUtils.isAllLowerCase(String.valueOf(simpleName.charAt(0)))) {
            char[] chars = simpleName.toCharArray();
            chars[0] -= 32;
            return String.valueOf(chars);
        }
        return simpleName;
    }


    public String builder() {
        //校验
        Assert.notNull(packageName, "包名不能为空");
        Assert.notNull(className, "类名不能为空");

        //构建
        StringBuilder javaContent = new StringBuilder();

        //包名
        javaContent.append(packageName).append(END);

        //导包
        for (String importName : importList) {
            javaContent.append(IMPORT).append(importName).append(END);
        }
        //类
        javaContent.append("public class ").append(className);


        //继承
        javaContent.append(StringUtils.isNoneBlank(extendsName) ? " extends " + extendsName : "");

        //实现
        if (implementList.size() > 0) {
            StringBuilder appendImportValue = new StringBuilder("implements ");
            for (String implementValue : implementList) {
                appendImportValue.append(implementValue).append(", ");
            }
            javaContent.append(" ").append(StringUtils.removeEnd(appendImportValue.toString(), ", "));
        }
        javaContent.append("{" + Ln);
        //属性
        for (String fields : fieldList) {
            javaContent.append(fields).append(END);
        }

        for (String method : methodList) {
            javaContent.append(method);
        }

        //类结尾
        javaContent.append("}" + Ln);

        return javaContent.toString();

    }


}
